import frappe
from frappe.model.document import Document
from frappe import _

@frappe.whitelist(allow_guest=False)
def close_sales_order_lines(sales_order, line_status, close_reason, idx_list):
    '''
    批量关闭销售订单行
    '''
    if isinstance(idx_list, str):
        import json
        idx_list = json.loads(idx_list)

    doc = frappe.get_doc("Sales Order", sales_order)
    user = frappe.get_doc("User", frappe.session.user)

    for item in doc.items:
        if item.idx in idx_list:
            item.custom_line_status = line_status
            item.custom_close_reason = close_reason
            item.custom_close_by = user.full_name
            item.custom_close_date = frappe.utils.now_datetime()

    doc.save(ignore_permissions=True)
    frappe.db.commit()

    # doc.status = "To Bill"
    # doc.delivery_status = "Fully Delivered"
    # doc.save(ignore_permissions=True)  # 保存更改
    # frappe.db.commit()  # 提交更改

    # 检查是否存在行状态为 NORMAL 或 PARTIAL_DELIVERY
    # has_normal_or_partial_delivery = any(
    #     item.custom_line_status in ["NORMAL", "PARTIAL_DELIVERY"] for item in doc.items
    # )

    # # 如果没有 NORMAL 或 PARTIAL_DELIVERY 状态的行，修改销售订单状态为 To Bill
    # if not has_normal_or_partial_delivery:
    #     # 调用标准 API 修改状态
    #     doc.status = "To Bill"
    #     doc.save(ignore_permissions=True)  # 保存更改
    #     frappe.db.commit()  # 提交更改

    return {"status": "success"}

from decimal import Decimal, ROUND_HALF_UP
@frappe.whitelist()
def manual_in_stock(sales_order_no, items):
    """
    手动入库接口（带校验）
    :param sales_order_no: 销售订单号
    :param items: 子项列表，格式：
        [
            {"item_code": "M0001", "idx": 1, "new_in_qty": 5},
            {"item_code": "M0002", "idx": 2, "new_in_qty": 3}
        ]
    """
    try:
        items = frappe.parse_json(items)
        so = frappe.get_doc("Sales Order", sales_order_no)

        updated_items = []
        update_log_rows = []

        success_msg=""
        for update in items:
            idx = update.get("idx")
            add_qty = Decimal(str(update.get("new_in_qty") or 0)).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)
            
            #行级锁
            frappe.db.sql("""
            SELECT name FROM `tabSales Order Item`
            WHERE parent=%s AND idx=%s FOR UPDATE
        """, (sales_order_no, idx))

            # 找对应行
            matched = next((i for i in so.items if i.idx == idx), None)
            if not matched:
                frappe.throw(f"未找到匹配的销售订单明细：{sales_order_no}（行号 {idx}）")

            # 原始数量
            prev_in_qty = Decimal(str(matched.custom_入库数量 or 0)).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)
            prev_out_qty = Decimal(str(matched.custom_可出库数量 or 0)).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)


            # 新数量
            new_in_qty = (prev_in_qty + add_qty).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)
            new_out_qty = (prev_out_qty + add_qty).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)

            # 校验逻辑：不能超过总数量
            if add_qty > 0 and new_in_qty >(Decimal(str(matched.qty or 0)) * Decimal('1.2')):
                frappe.throw(f"入库数量 {new_in_qty} 超出物料 {matched.item_code} 的订购数量 {matched.qty} 的20%")
            
            # 更新字段
            matched.custom_入库数量 = new_in_qty
            # if add_qty >0:
            matched.custom_可出库数量 = new_out_qty  

            # 日志记录
            update_log_rows.append({
                "type": "manual",
                "sales_order": sales_order_no,
                "idx": idx,
                "item_code": matched.item_code,
                "update_before_item_in_val": prev_in_qty,
                "update_before_item_out_val": prev_out_qty,
                "update_after_item_in_val": matched.custom_入库数量,
                "update_after_item_out_val": matched.custom_可出库数量,
                "update_count":add_qty,
                "produce_line":update.get("produce_line"),
                "work_center": None,
                "work_center_desc": None
            })

            updated_items.append({
                "idx": idx,
                "item_code": matched.item_code,
                "new_in_qty": new_in_qty,
                "new_out_qty": new_out_qty,
                "update_count":add_qty,
            })
            success_msg+=f"物料{matched.item_code}成功入库{add_qty},入库前数量:{prev_in_qty}->入库后数量{new_in_qty},入库前可出库数量:{prev_out_qty}->入库后可出库数量:{new_out_qty}\n"
        # 保存主单据
        so.save()

        success_msg=success_msg[:-1]

        # 插入日志
        for log_row in update_log_rows:
            try:
                frappe.get_doc({
                    "doctype": "SO Stock Update Log",
                    **log_row,
                    "posting_datetime": frappe.utils.now_datetime()
                }).insert(ignore_permissions=True)
            except Exception:
                frappe.local.response["http_status_code"] = 500
                frappe.logger().error(frappe.get_traceback(), f"写入 SO Stock Update Log 失败：{log_row}")

        frappe.db.commit()
        # frappe.db.rollback()

        return {
            "status": "success",
            "message": success_msg,
            "updated_items": updated_items
        }

    except Exception as e:
        frappe.db.rollback()
        frappe.log_error(frappe.get_traceback(), "手动入库失败")
        frappe.local.response["http_status_code"] = 500
        return {
            "status": "error",
            "message": str(e)
        }
